#include "RingQueue.hpp"
#include <iostream>
#include <pthread.h>
#include <cstdlib>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>

/**
 * 提问：多生产多消费的意义在哪里？
 * 答：不要狭隘的认为，把任务或者数据放在交易场所，就是生产和消费啦！
 *     将数据或者任务生产前和拿到之后处理，才是最耗费时间的。
 *     生产的本质：私有的任务-> 公共空间中
 *     消费的本质：公共空间中的任务-> 私有的
 *     意义在于：可以并发的生产任务或处理任务，而不是简单的领取任务！
 *     实际示例：如食堂吃饭：阿姨的盆里是食物，是公共空间！
 *              你排队打饭打菜是：领取资源的过程，相当于领任务！
 *              拿到饭之后，你就可以开始吃了！此时食堂内的所有干饭者是同时吃饭的！即并行处理饭菜！
 * 
 * 提问：信号量的本质是一个计数器，计数器的意义是什么？
 * 答：意义：可以不用进入临界区，就可以得知资源情况，甚至可以减少临界区内部的判断！
 *      此前的操作（使用线程控制的基本方法）：申请锁 => 判断与访问 => 释放锁
 *         本质：是我们并不清楚临界资源的情况！
 *      信号量是要提前预设资源情况！而且在pv变化过程中，我们可以在外部就知道临界资源的情况！
*/

/* 消费者 */
void *consumer(void* args){
    RingQueue<int> *rq = (RingQueue<int>*)args;

    while(true){
        int x;
        // 1. 从环形队列中获取任务或数据
        rq->pop(&x);

        // 2. 进行一定的处理：不要忽略时间消耗问题
        std::cout << "消费：" << x << " => [ " << pthread_self() << " ]" << std::endl;

        // sleep(1);       // （单线程版）消费速度限制：预期现象，一下子生产占满队列后，一消费一生产的循环状态
        sleep(1);
    }
}

/* 生产者 */
void *productor(void* args){
    RingQueue<int> *rq = (RingQueue<int>*)args;

    while(true){
        // sleep(1);           // （单线程版）生产速度限制：预期现象，一生产一消费的循环状态，一定是先生产

        // 1. 构建数据或任务对象：一般可以从外部获取 / 不要忽略时间消耗问题
        // int x = 10;
        int x = rand() % 100 +1;
        std::cout << "生产：" << x << " => [ " << pthread_self() << " ]" <<  std::endl;

        // 2. 推送到环形队列中
        rq->push(x);                                            // 完成生产过程
    }
}

int main(){

    srand((uint64_t)time(nullptr)^getpid()^0x357);

    // 环形队列对象
    RingQueue<int>* rq = new RingQueue<int>();
    // 测试对象初步设计
    // rq->debug();             //OK

    // 环形队列（单生产者消费者模式）：需要两个线程
#if 0
    pthread_t t1, t2;
    pthread_create( &t1, nullptr, consumer, (void*)rq);         // 传入环形队列实现线程资源共享
    pthread_create( &t2, nullptr, productor, (void*)rq);

    pthread_join( t1, nullptr);
    pthread_join( t2, nullptr);
#endif

    // 环形队列（多生产者消费者模式）：示例：5个消费者，3个生产者
#if 1
    pthread_t c[5], p[3];    
    pthread_create(c    , nullptr, consumer, (void*)rq);
    pthread_create(c + 1, nullptr, consumer, (void*)rq);
    pthread_create(c + 2, nullptr, consumer, (void*)rq);
    pthread_create(c + 3, nullptr, consumer, (void*)rq);
    pthread_create(c + 4, nullptr, consumer, (void*)rq);

    pthread_create(p    , nullptr, productor, (void*)rq);
    pthread_create(p + 1, nullptr, productor, (void*)rq);
    pthread_create(p + 2, nullptr, productor, (void*)rq);

    for(int i = 0; i < 5; i++) pthread_join(c[i], nullptr);
    for(int i = 0; i < 3; i++) pthread_join(p[i], nullptr);

#endif   
    return 0;
}